package org.easypool;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public final class PoolDataSource extends DataSource {

    private Logger logger = LoggerFactory.getLogger(PoolDataSource.class);

    // 获取连接线程等待的时间
    private static  Integer GET_CONNECTION_WAIT_MAX_TIME;
    // 连接的寿命 (default 10s)
    private static  Integer CONNECT_ACTIVE_MAX_TIME;

    private static  Integer MAX_POOL_SIZE;

    private static  String PING_QUERY;


    private PoolState poolState = new PoolState();


    public PoolDataSource (DataSourceConfig dataSourceConfig) {
        super(dataSourceConfig);
        PoolDataSource.GET_CONNECTION_WAIT_MAX_TIME = dataSourceConfig.getGET_CONNECTION_WAIT_MAX_TIME();
        PoolDataSource.CONNECT_ACTIVE_MAX_TIME = dataSourceConfig.getCONNECT_ACTIVE_MAX_TIME();
        PoolDataSource.MAX_POOL_SIZE = dataSourceConfig.getMAX_POOL_SIZE();
        PoolDataSource.PING_QUERY = dataSourceConfig.getPING_QUERY();
    }


    public void pushConnection(PoolConnection conn) {
        synchronized (poolState) {
            poolState.activeConnections.remove(conn);
            if (conn.isValid()) {
                // 空闲队列没有满
                if (poolState.idleConnections.size() < MAX_POOL_SIZE) {
                    try {
                        if (!conn.getRealConnection().getAutoCommit()) {
                            conn.getRealConnection().rollback();
                        }
                        PoolConnection newConn = new PoolConnection(conn.getRealConnection(), this);
                        poolState.idleConnections.add(newConn);
                        conn.invalidate();
                        poolState.notifyAll();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    try {
                        if (!conn.getRealConnection().getAutoCommit()) {
                            conn.getRealConnection().rollback();
                        }
                        conn.getRealConnection().close();
                        conn.invalidate();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public boolean pingConnection(PoolConnection conn) {
        boolean result = true;
        Connection realConnection = null;
        try {

            if ((realConnection = conn.getRealConnection()).isClosed()) result = false;

        } catch (Exception e) {
            e.printStackTrace();
            result = false;
        }

        if (result) {
            try {

                Statement statement = realConnection.createStatement();
                ResultSet resultSet = statement.executeQuery(PING_QUERY);
                resultSet.close();
                statement.close();

                if (!realConnection.getAutoCommit()) realConnection.rollback();
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    conn.getRealConnection().close();
                } catch (Exception e2) {
                    //ignore
                }
                result = false;
            }

        }

        return result;
    }

    private void forceCloseAll() {
        synchronized (poolState) {
            for (int i = poolState.activeConnections.size(); i >= 0; i--) {

                try {
                    PoolConnection poolConnection = poolState.activeConnections.remove(i);
                    poolConnection.invalidate();
                    Connection realCon = poolConnection.getRealConnection();
                    if (!realCon.getAutoCommit()) realCon.rollback();
                    realCon.close();
                } catch (Exception e) {

                }
            }
            for (int i = poolState.idleConnections.size(); i >= 0; i--) {
                try {
                    PoolConnection poolConnection = poolState.idleConnections.remove(i);
                    poolConnection.invalidate();
                    Connection realCon = poolConnection.getRealConnection();
                    if (!realCon.getAutoCommit()) realCon.rollback();
                    realCon.close();
                } catch (Exception e) {

                }
            }
        }
    }

    protected void finalize() throws Throwable {
        forceCloseAll();
        super.finalize();
    }

    @Override
    public Connection getConnection() throws SQLException {
        return popConnection().getProxyConnection();
    }

    private PoolConnection popConnection() throws SQLException {
        PoolConnection conn = null;

        while (conn == null) {
            synchronized (poolState) {
                // 如果空闲队列不为空
                if (poolState.idleConnections.size() > 0) {
                    conn = poolState.idleConnections.remove(0);
                } else {
                    // 连接队列没有满
                    if (poolState.activeConnections.size() < MAX_POOL_SIZE) {
                        conn = new PoolConnection(super.getConnection(), this);
                    } else {
                        // 队列满了
                        Long bornTimeMillis = poolState.activeConnections.get(0).getBornTimeMillis();
                        Long nowTimeMillis = System.currentTimeMillis();
                        if ((nowTimeMillis - bornTimeMillis) > CONNECT_ACTIVE_MAX_TIME) {
                            // 移除超时连接
                            PoolConnection poolConnection = poolState.activeConnections.remove(0);
                            if (!poolConnection.getRealConnection().getAutoCommit())
                                poolConnection.getRealConnection().rollback();
                            poolConnection.getRealConnection().close();
                        } else {
                            // 让获取连接的线程进行等待
                            try {
                                poolState.wait(GET_CONNECTION_WAIT_MAX_TIME);
                            } catch (Exception e) {
                                e.printStackTrace();
                                break;
                            }
                        }

                    }
                }

                if (conn != null) {
                    if (conn.isValid()) {
                        if (!conn.getRealConnection().getAutoCommit()) conn.getRealConnection().rollback();
                        conn.setBornTimeMillis(System.currentTimeMillis()); // 重新设置出生时间
                        poolState.activeConnections.add(conn);
                    }
                }
            }
        }

        if (conn == null) {
            throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
        }

        return conn;
    }
}
